package com.wcs.exam.common.server.sms.impl;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wcs.exam.common.server.sms.Sms;
import com.wcs.exam.common.server.sms.SmsFace;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 华为云
 *
 * @author wcs
 */
@Slf4j
@Component(value = "huaweiSms")
public class HuaweiCloudSmsImpl implements SmsFace {
    /**
     * 无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
     */
    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
    /**
     * 无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
     */
    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";


    @Override
    public Boolean sendVerCode(String mobile, String code, Sms sms) {
        return sendSms(mobile, new String[]{code}, sms.getHuaweiSmsSignName(), sms.getHuaweiSmsSender(), sms.getHuaweiSmsAuthCode(), sms.getHuaweiSmsAccessKeyId(), sms.getHuaweiSmsAccessKeySecret());
    }

    private static Boolean sendSms(String phone, String[] templateParam, String signName, String sender, String templateId, String appKey, String appSecret) {
        //必填,请参考"开发准备"获取如下数据,替换为实际值
        String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendDiffSms/v1";

        //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
        String[] receiver = new String[]{phone};

        //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
        String statusCallBack = "";

        /*
         * 选填,使用无变量模板时请赋空值 String[] templateParas = {};
         * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为{"369751"}
         * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为{"3","人民公园正门"}
         * ${DATE}${TIME}变量不允许取值为空,${TXT_20}变量可以使用英文空格或点号替代空值,${NUM_6}变量可以使用0替代空值
         * 查看更多模板和变量规范:产品介绍>模板和变量规范
         */

        //smsContent,不携带签名名称时,signature请填null
        List<Map<String, Object>> smsContent = new ArrayList<>();
        Map<String, Object> item1 = initDiffSms(receiver, templateId, templateParam, signName);
        if (null != item1 && !item1.isEmpty()) {
            smsContent.add(item1);
        }

        //请求Body
        String body = buildRequestBody(sender, smsContent, statusCallBack);
        if (null == body || body.isEmpty()) {
            return false;
        }

        //请求Headers中的X-WSSE参数值
        String wsseHeader = buildWsseHeader(appKey, appSecret);
        Writer out = null;
        BufferedReader in = null;
        StringBuffer results = new StringBuffer();
        HttpsURLConnection connection;
        InputStream is = null;

        try {
            URL realUrl = URI.create(url).toURL();
            connection = (HttpsURLConnection) realUrl.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(true);
            //请求方法
            connection.setRequestMethod("POST");
            //请求Headers参数
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
            connection.setRequestProperty("X-WSSE", wsseHeader);
            connection.connect();
            out = new OutputStreamWriter(connection.getOutputStream());
            out.write(body);
            out.flush();
            out.close();

            int status = connection.getResponseCode();
            if (200 == status) {
                is = connection.getInputStream();
            } else {
                // 400/401
                is = connection.getErrorStream();
            }
            in = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
            String line;
            while ((line = in.readLine()) != null) {
                results.append(line);
            }
            JSONObject rs = JSONUtil.parseObj(results);
            if (!rs.getStr("code").equals("000000")) {
                log.error(rs.getStr("description"));
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("请求失败", e);
        } finally {
            try {
                if (null != out) {
                    out.close();
                }
                if (null != is) {
                    is.close();
                }
                if (null != in) {
                    in.close();
                }
            } catch (Exception e) {
                log.error("关闭失败", e);
            }
        }
        return false;
    }

    /**
     * 构造smsContent参数值
     *
     * @param receiver      短信接收方的号码
     * @param templateId    短信模板ID
     * @param templateParas 短信模板的变量值列表
     * @param signature     签名名称，必须是已审核通过的，与模板类型一致的签名名称。
     * @return 发送参数
     */
    private static Map<String, Object> initDiffSms(String[] receiver, String templateId, String[] templateParas, String signature) {
        if (null == receiver || null == templateId || receiver.length == 0 || templateId.isEmpty()) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        map.put("to", receiver);
        map.put("templateId", templateId);
        if (null != templateParas && templateParas.length > 0) {
            map.put("templateParas", templateParas);
        }
        if (null != signature && !signature.isEmpty()) {
            map.put("signature", signature);
        }
        return map;
    }

    /**
     * 构造请求Body体
     *
     * @param sender         发送短信的号码
     * @param smsContent     短信内容
     * @param statusCallBack 用户的回调地址，用于接收短信状态报告
     * @return 参数值
     */
    private static String buildRequestBody(String sender, List<Map<String, Object>> smsContent, String statusCallBack) {
        if (null == sender || null == smsContent || sender.isEmpty() || smsContent.isEmpty()) {
            return null;
        }
        JSONArray jsonArr = new JSONArray();
        for (Map<String, Object> it : smsContent) {
            jsonArr.put(it);
        }

        Map<String, Object> data = new HashMap<>();
        data.put("from", sender);
        data.put("smsContent", jsonArr);
        if (null != statusCallBack && !statusCallBack.isEmpty()) {
            data.put("statusCallback", statusCallBack);
        }

        return new JSONObject(data).toString();
    }

    private static String buildWsseHeader(String appKey, String appSecret) {
        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String time = sdf.format(new Date());
        String nonce = UUID.randomUUID().toString().replace("-", "");
        MessageDigest md;
        byte[] passwordDigest = null;
        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update((nonce + time + appSecret).getBytes());
            passwordDigest = md.digest();
        } catch (NoSuchAlgorithmException e) {
            log.error("处理失败", e);
        }

        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest);
        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
    }
}
